﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Serialization;

namespace CrawlerFramework.ConfigEntity
{
    public class Url
    {
        [XmlAttribute("context")]
        public string Context = string.Empty;
    }

    [XmlRoot("link-config")]
    public class LinkConfig
    {
        /// <summary>
        /// The name of the assembly in which the PageProcess concrete class is defined
        /// </summary>
        [XmlElement("assembly-name")]
        public string AssemblyName = string.Empty;

        /// <summary>
        /// The name of the file that defined the information structure in xml
        /// </summary>
        [XmlElement("config-entity-file")]
        public string ConfigEntityFile = string.Empty;

        /// <summary>
        /// The patterns that are used to filters the links that should not be crawled.
        /// </summary>
        [XmlArray("filters")]
        [XmlArrayItem("match", typeof(Match))]
        public List<Match> Filters = new List<Match>();

        /// <summary>
        /// Full name of the PageProcess concrete class, including Namespace and class name.
        /// </summary>
        [XmlElement("full-class-name")]
        public string FullClassName = string.Empty;

        /// <summary>
        /// The GUID of LinkConfig. This GUID is referenced in ScheduledJob entity, also this is the name of the xml file
        /// </summary>
        [XmlAttribute("guid")]
        public Guid Guid = Guid.Empty;

        /// <summary>
        /// List of links that are specified directly by developers.
        /// </summary>
        [XmlArray("urls")]
        [XmlArrayItem("url", typeof(Url))]
        public List<Url> Urls = new List<Url>();

        /// <summary>
        /// The patterns that are used to match appropriate links stored in DB
        /// </summary>
        [XmlArray("matches")]
        [XmlArrayItem("match", typeof(Match))]
        public List<Match> Matches = new List<Match>();

        /// <summary>
        /// Get the file path to the ConfigEntityFile
        /// </summary>
        /// <returns></returns>
        public string BuildEntityConfigPath()
        {
            if (string.IsNullOrEmpty(ConfigEntityFile))
                throw new FileNotFoundException("Not specified config file path");

            var configFolder = ConfigurationManager.AppSettings["XmlConfigFolder"];
            if (string.IsNullOrEmpty(configFolder))
                configFolder = Common.DefaultXmlConfigFolder;

            return Path.Combine(configFolder, ConfigEntityFile);
        }

        /// <summary>
        /// Determine whether the url is sastified to be crawled
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public bool IsSatisfy(string url)
        {
            var isMatch = Matches.Any(m => m.IsMatch(url));
            var isExcluded = Filters.Any(m => m.IsMatch(url));

            return isMatch && !isExcluded;
        }
    }

    public class Match
    {
        [XmlAttribute("isMatchExact")]
        public bool IsMatchExact;

        [XmlAttribute("pattern")]
        public string Pattern = string.Empty;

        public bool IsMatch(string input)
        {
            if (IsMatchExact)
                return Pattern.Equals(input);

            var regex = new Regex(Pattern);
            return regex.Match(input).Success;
        }
    }
}